/**
 * @file xf_main.c
 * @author your name (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2023-10-23
 *
 * Copyright (c) 2023, CorAL. All rights reserved.
 *
 */

/* ==================== [Includes] ========================================== */

#include "xf_hal.h"
#include "lcd_drive.h"
#include "xf_osal.h"
#include "lvgl.h"
#include "xf_task.h"
#include "xf_log.h"
#include "srceen_menu.h"
#include "xf_sys.h"
#include "lv_demos.h"

/* ==================== [Defines] =========================================== */

#define LCD_SDA_PIN_NUM             (1)
#define LCD_SCL_PIN_NUM             (6)
#define LCD_CS_PIN_NUM              (0)
#define LCD_RST_PIN_NUM             (2)
#define LCD_DC_PIN_NUM              (5)
#define LCD_BL_PIN_NUM              (4)

#define LCD_SPI_NUM                 (1)

#define DISP_BUF_SIZE               (240 * 90 * 2)

#define LEFT_BTN_IO         3
#define MIDDLE_BTN_IO       15
#define RIGHT_BTN_IO        16

#define TAG "xf_main"

/* ==================== [Typedefs] ========================================== */

/* ==================== [Static Prototypes] ================================= */

static xf_err_t lcd_write_cmd(uint8_t *cmd, uint32_t len);
static xf_err_t lcd_write_data(uint8_t *data, uint32_t len);
static xf_err_t lcd_reset(void);
static xf_err_t lcd_backlight(bool is_on);
static void lvgl_task(xf_task_t task);
static void display_init(int32_t hor_res, int32_t ver_res);
static void flush_cb(lv_display_t *display, const lv_area_t *area, void *px_map);
#if LV_USE_LOG
    static void lv_log_print_g_cb(lv_log_level_t level, const char *buf);
#endif
static void lv_timer_cb(lv_timer_t *t);

/* ==================== [Static Variables] ================================== */

static lcd_t s_lcd = NULL;
const static lcd_drive_in_t s_lcd_in = {
    .write_cmd = lcd_write_cmd,
    .write_data = lcd_write_data,
    .reset = lcd_reset,
    .backlight = lcd_backlight
};

/* ==================== [Macros] ============================================ */

/* ==================== [Global Functions] ================================== */

void xf_main(void)
{
    xf_hal_spi_gpio_t master_gpio = {
        .mosi_num = LCD_SDA_PIN_NUM,
        .miso_num = -1,
        .sclk_num = LCD_SCL_PIN_NUM,
        .cs_num = LCD_CS_PIN_NUM,
        .quadhd_num = XF_HAL_GPIO_NUM_NONE,
        .quadwp_num = XF_HAL_GPIO_NUM_NONE,
    };
    xf_hal_spi_init(LCD_SPI_NUM, XF_HAL_SPI_HOSTS_MASTER, 40 * 1000 * 1000);
    xf_hal_spi_set_gpio(LCD_SPI_NUM, &master_gpio);
    xf_hal_spi_set_mode(LCD_SPI_NUM, XF_HAL_SPI_MODE_0);
    xf_hal_spi_enable(LCD_SPI_NUM);

    xf_hal_gpio_init(LCD_RST_PIN_NUM, XF_HAL_GPIO_DIR_OUT);
    xf_hal_gpio_init(LCD_DC_PIN_NUM, XF_HAL_GPIO_DIR_OUT);
    xf_hal_gpio_init(LCD_BL_PIN_NUM, XF_HAL_GPIO_DIR_OUT);

    s_lcd = lcd_drive_create(&s_lcd_in);
    lcd_drive_init(s_lcd, SCR_CONTROLLER_st7789, 240, 320);
    lcd_set_rotation(s_lcd, SCR_DIR_BTLR);

    xf_hal_gpio_init(LEFT_BTN_IO, XF_HAL_GPIO_DIR_IN);
    xf_hal_gpio_init(MIDDLE_BTN_IO, XF_HAL_GPIO_DIR_IN);
    xf_hal_gpio_init(RIGHT_BTN_IO, XF_HAL_GPIO_DIR_IN);
    xf_hal_gpio_set_pull(LEFT_BTN_IO, XF_HAL_GPIO_PULL_UP);

    lv_init();
    display_init(320, 240);
#if LV_USE_LOG
    lv_log_register_print_cb(lv_log_print_g_cb);
#endif

    lv_tick_set_cb(xf_sys_time_get_ms);
    xf_ntask_create_loop(lvgl_task, NULL, 10, 10);

    create_screens();
    load_screen();
    // lv_demo_widgets();
    // lv_demo_keypad_encoder();
    xf_sys_watchdog_disable();

    lv_timer_create(lv_timer_cb, 100, NULL);
}

/* ==================== [Static Functions] ================================== */

#if LV_USE_LOG
static void lv_log_print_g_cb(lv_log_level_t level, const char *buf)
{
    xf_log_level("lvgl", level, "%s", buf);
}
#endif

static void lvgl_task(xf_task_t task)
{
    lv_timer_handler();
}

static xf_err_t lcd_write_cmd(uint8_t *cmd, uint32_t len)
{
    xf_err_t err = XF_OK;
    err |= xf_hal_gpio_set_level(LCD_DC_PIN_NUM, 0);
    xf_hal_spi_write(LCD_SPI_NUM, cmd, len, 1000);

    return err;
}

static xf_err_t lcd_write_data(uint8_t *data, uint32_t len)
{
    xf_err_t err = XF_OK;
    err |= xf_hal_gpio_set_level(LCD_DC_PIN_NUM, 1);
    xf_hal_spi_write(LCD_SPI_NUM, data, len, 1000);

    return err;
}

static xf_err_t lcd_reset(void)
{
    xf_err_t err = XF_OK;
    XF_LOGI(TAG, "lcd reset");
    err |= xf_hal_gpio_set_level(LCD_RST_PIN_NUM, 1);
    xf_osal_delay_ms(100);
    err |= xf_hal_gpio_set_level(LCD_RST_PIN_NUM, 0);
    xf_osal_delay_ms(100);
    err |= xf_hal_gpio_set_level(LCD_RST_PIN_NUM, 1);
    if (err != XF_OK) {
        XF_LOGE(TAG, "lcd reset failed");
    }

    return err;
}

static xf_err_t lcd_backlight(bool is_on)
{
    xf_err_t err = XF_OK;
    err = xf_hal_gpio_set_level(LCD_BL_PIN_NUM, is_on);
    if (err != XF_OK) {
        XF_LOGE(TAG, "lcd backlight failed");
    }
    return err;
}

static void display_init(int32_t hor_res, int32_t ver_res)
{
    lv_display_t *disp = lv_display_create(hor_res, ver_res);
    void *buf = malloc(DISP_BUF_SIZE);
    lv_display_set_flush_cb(disp, flush_cb);
    lv_display_set_buffers(disp, buf, NULL, DISP_BUF_SIZE, LV_DISPLAY_RENDER_MODE_PARTIAL);
}

static void flush_cb(lv_display_t *display, const lv_area_t *area, void *px_map)
{
    /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one
     *`put_px` is just an example, it needs to be implemented by you.*/
    uint16_t *buf16 = (uint16_t *)px_map;  /*Let's say it's a 16 bit (RGB565) display*/
    lv_draw_sw_rgb565_swap(buf16, (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1));
    lcd_drive_draw_bitmap(s_lcd, area->x1, area->y1, area->x2 - area->x1 + 1, area->y2 - area->y1 + 1, buf16);
    /* IMPORTANT!!!
     * Inform LVGL that you are ready with the flushing and buf is not used anymore*/
    lv_display_flush_ready(display);
}

static void lv_timer_cb(lv_timer_t *t)
{
    static bool last_left_button_pressed = true; // 记录上一次左拨按钮的状态
    static bool last_right_button_pressed = true; // 记录上一次右拨按钮的状态
    static bool last_middle_button_pressed = true; // 记录上一次中拨按钮的状态

    // 读取按钮状态
    bool left_button_pressed = xf_hal_gpio_get_level(LEFT_BTN_IO);
    bool right_button_pressed = xf_hal_gpio_get_level(RIGHT_BTN_IO);
    bool middle_button_pressed = xf_hal_gpio_get_level(MIDDLE_BTN_IO);
    // 根据按钮状态模拟旋钮操作
    if (last_left_button_pressed == true && left_button_pressed == false) {
        // 模拟左拨
        menu_next();
    }

    if (last_right_button_pressed == true && right_button_pressed == false) {
        // 模拟右拨
        menu_prev();
    }

    if (last_middle_button_pressed == true && middle_button_pressed == false) {
        // 模拟按下
        menu_select();
    }
    
    last_left_button_pressed = left_button_pressed;
    last_right_button_pressed = right_button_pressed;
    last_middle_button_pressed = middle_button_pressed;
}
